<?php
/* =====================================================================

===================================================================== */

class EDatabase extends Exception {};
class EDatabaseGeneral extends EDatabase {};
class EDatabaseConnectionFailure extends EDatabaseGeneral {};
class EDatabaseSelectionFailure extends EDatabaseGeneral {};
class EDatabaseSpecific extends EDatabase {};
class EDatabaseQueryError extends EDatabaseSpecific {};


abstract class MySQLDatabase
{
	var $Connection;
	var $RowIndices;
	var $RowCount;
	var $CurrentRow;
	var $CurrentDBResult;
	var $PassthroughError;
	var $LastID;
	var $LastError;

	public function __construct()
	{
		$this->Connection=0;
		$this->RowIndices[] = "";
		$this->RowCount=0;
		$this->CurrentRow=0;
		$this->CurrentDBResult=0;
		$this->PassthroughError = FALSE;
		$this->LastError = "";
	}

	abstract protected function getDatabaseName();

	abstract protected function getUserName();

	abstract protected function getPassword();

	abstract protected function getSiteTitle();

	public function __destruct()
	{
		if ($this->Connection) {
			@mysqli_close($this->Connection);
		}
	}

	private function reportTitle($title)
	{
		$len = strlen($title);
		$title = strtoupper($title);
		$number = (70-($len+2)) / 2;
		return str_repeat("-", $number)." $title ".str_repeat("-", $number)."\n";
	}

	protected function reportError ($ErrorText="", $SQL="")
	{
		include("header.php");
		print("<H2>A System Problem occured</H2><br />");
		print("Sorry, there was a problem completing your request. <br /><br />");
		print("Error: $ErrorText<br />");
		print("SQL: $SQL");
		include("footer.php");
		exit();
		$callers = debug_backtrace();
		array_shift($callers);
		foreach ($callers as $item) {
			$backtrace .= "$item[file]:$item[line]\n";
			$backtrace .= "$item[function](";
			$args = 0;
			foreach ($item[args] as $arg) {
				if ($args)
					$backtrace .= ", ";
				$backtrace .= "'$arg'";
				$args++;
			}
			$backtrace .= ");\n\n";
		}
		try {
			foreach ($_POST as $key=>$value) {
				$post .= "\$$key = $value\n";
			}
			foreach ($_GET as $key=>$value) {
				$get .= "\$$key = $value\n";
			}
			foreach ($_SERVER as $key=>$value) {
				$server .= "\$$key = $value\n";
			}
			foreach ($_SESSION as $key=>$value) {
				if (!is_object($value)) $session .= "\$$key = $value\n";
			}
		}
		catch (Exception $e) {
			// Do nothing
		}

		mail('jaskiewicz.kamil@gmail.com', $this->getSiteTitle()." Database error",
			$this->reportTitle("Error").wordwrap($ErrorText,70)."\nError Number: $ErrorNumber\n\n".
			$this->reportTitle("SQL")."$SQL\n\n".
			$this->reportTitle("Function Backtrace")."$backtrace\n\n".
			$this->reportTitle("Post")."$post\n\n".
			$this->reportTitle("Get")."$get\n\n".
			$this->reportTitle("Server")."$server".
			$this->reportTitle("Session")."$session",
			"From: ".$this->getSiteTitle()."");
	}

	public function getConnection()
	{
		if ($this->Connection && @mysqli_ping($this->Connection)) {
			return $this->Connection;
		}

		if (file_exists("/tmp/mysql.sock")) {
			$server = "localhost:/tmp/mysql.sock";
		}
		else {
			$server = "localhost";
		}

		$this->Connection = mysqli_connect($server, $this->getUserName(), $this->getPassword());
		if (!$this->Connection) {
			sleep(1);
			$this->Connection = @mysqli_connect($server, $this->getUserName(), $this->getPassword());
			if (!$this->Connection) {
				throw new EDatabaseConnectionFailure("Database connection failed");
			}
		}

		return $this->Connection;
	}

	protected function logQuery()
	{
		if (file_exists("/tmp/sql.log") && is_writable("/tmp/sql.log")) {
			$fp=fopen("/tmp/sql.log", "a");
			$callers = debug_backtrace();
			fputs($fp, $this->DBName."[".date("d m Y, h:i:s")."]: $this->LastSQLStatement (");
			foreach ($callers as $call) {
				fputs($fp, $call[file].":".$call[line].", ");
			}
			fputs($fp, ")\r\n");
			fclose($fp);
		}
	}

	public function getCallerComment()
	{
		$callers = debug_backtrace();
		foreach ($callers as $call) {
			if (strstr($call["file"], "/Database.class.php") === false &&
				strstr($call["file"], "/MySQLDatabase.class.php") === false)
				return "/*".basename($call["file"]).":".$call["line"]."*/";
		}
	}

	public function query($SQL, $PageNumber="", $ItemsPerPage="")
	{

		// Get a database connection (or re-use an existing one).
		$this->getConnection();
		if (!@mysqli_select_db($this->Connection, $this->getDatabaseName())) {
			throw new EDatabaseSelectionFailure("Database selection failed for ".$this->getDatabaseName());
		}

		$this->LastSQLStatement = $SQL;

		// Log the query to /tmp/sql.log if available
		$this->logQuery();

		$caller = $this->getCallerComment();

		// If a page number is specified, run the query once without the LIMIT clause to determine total pages and total records
		if ($PageNumber) {
			$StartItem = ($PageNumber - 1) * $ItemsPerPage;
			$Result=mysqli_query($this->Connection, "$caller $SQL");
			$this->TotalRowCount = @mysqli_num_rows($Result);
			$this->TotalPageCount = floor($this->TotalRowCount/$ItemsPerPage);
			if (($this->TotalPageCount * $ItemsPerPage) != $this->TotalRowCount) {
				$this->TotalPageCount++;
			}
			unset($Result);
			$SQL .= " LIMIT $StartItem, $ItemsPerPage";
		}

		$result = mysqli_query($this->Connection, "$caller $SQL");
		// Get either the number of rows returned or affected, depending on the query type
		// If it's a select query also remember the result identifier for later iteration
		// If it's an insert query (or other) remember the last inserted auto_increment field
		if (eregi("^select", $SQL) || eregi("^desc", $SQL)) {
			$this->RowCount = @mysqli_num_rows($result);
			$this->CurrentDBResult = $result;
		}
		else if (eregi("^insert", $SQL)) {
			$idres = mysqli_query($this->Connection, "SELECT LAST_INSERT_ID()");
			list($this->LastID) = mysqli_fetch_array($idres);
		}
		else {
			$this->RowCount = @mysqli_affected_rows($result);
		}

		$error = mysqli_error($this->Connection);
		if ($error) {
			$this->reportError($error, $SQL);
			throw new EDatabaseQueryError($error);
		}
	}

	public function callProcedure($proc)
	{
		// Get a database connection (or re-use an existing one).
		$this->getConnection();
		if (!@mysqli_select_db($this->Connection, $this->getDatabaseName())) {
			throw new EDatabaseSelectionFailure("Database selection failed for ".$this->getDatabaseName());
		}

		$SQL = "call $proc";
		$this->LastSQLStatement = $SQL;

		// Log the query to /tmp/sql.log if available
		$this->logQuery();

		mysqli_multi_query($this->Connection, $SQL);
		$result = mysqli_store_result($this->Connection);

		if (mysqli_more_results($this->Connection)) {
			$this->CurrentDBResult = $result;
			return true;
		}

		$error = mysqli_error($this->Connection);
		if ($error) {
			$this->reportError($error, $SQL);
			throw new EDatabaseQueryError($error);
		}
	}

	public function clearQuery () {
		$this->RowCount=0;
		$this->CurrentDBResult=0;
	}

	public function getNextRow()
	{
		return mysqli_fetch_array($this->CurrentDBResult);
	}

	public function resetRecordPointer()
	{
		return mysqli_data_seek($this->CurrentDBResult, 0);
	}

	public function finish()
	{
		if ($this->Connection) {
			@mysqli_close($this->Connection);
			$this->Connection = 0;
			$this->RowCount = 0;
			$this->CurrentRow = -1;
			$this->CurrentDBResult = 0;
		}
	}

	public function saveLoopResult()
	{
		$this->LoopResult = $this->CurrentDBResult;
	}

	public function restoreLoopResult()
	{
		$this->CurrentDBResult = $this->LoopResult;
	}

	function getInstance() // this implements the 'singleton' design pattern.
    {
        static $instance;

        if (!isset($instance)) {
            $instance = new Database();
        }

        return $instance;
    }

	protected function reversehtmlentities($string)
	{
		$trans_tbl = get_html_translation_table(HTML_ENTITIES);
		$trans_tbl = array_flip($trans_tbl);
		$ret = strtr($string, $trans_tbl);
		return preg_replace('/&#(\d+);/me' , "chr('\1')" , $ret);
	}

	public function makeFieldSafe($string)
	{
		if (get_magic_quotes_gpc()) {
			$string = stripslashes($string);
		}

		$string = mysqli_real_escape_string($this->getConnection(), $string);
		$string = $this->reversehtmlentities($string);
		if (substr($string, -1, 1) == "\\") {
			$string = substr($string, 0, -1);
		}
		return ereg_replace('£', '&pound;', $string);
	}


}

?>
